
#region Imported Namespaces
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
#endregion

namespace PHSRAG.WebControls
{
	#region Class PostbackGuard
	/// <summary>
	/// The PostbackGuard control allows the detection of two events; one, a page refresh (instigated through the
	/// browser), and two, a new postback while an earlier postback is still in progress on the server.
	/// In either case, it would probably be not a good idea if the page handling the postback blindly did
	/// whatever it did without considering the aforementioned events; especially if the postback somehow leads
	/// to activities such as inserting into database tables, etc.
	/// </summary>
	/// <remarks>
	/// <p>Any page which wants to guard against the postback should include an instance of this control on it.
	/// In its postback handler, the page should invoke the IsValid property of the PostbackGuard control.</p>
	/// <p>If that property returns True, then it is OK for the page to proceed normally. If it is False, then
	/// either the page has been refreshed or the current postback overlaps a previous postback, and the page
	/// should exercise caution.</p>
	/// </remarks>
	/// <example>
	/// The following example shows how the page could use the IsValid property.
	/// <p>In the HTML for the page:</p>
	/// <code>
	/// <![CDATA[
	/// <%@ Register TagPrefix="cc1" Namespace="PHSRAG.WebControls" Assembly="PHSRAG.WebControls" %>
	/// ...
	/// <body>
	/// ...
	/// <cc1:PostbackGuard id="postbackGuard" runat="server" />
	/// ...
	/// </body>
	/// ]]>
	/// </code>
	/// <p>In the code-behind for the page:</p>
	/// <code>
	/// <![CDATA[
	/// private void OnPageLoad(object sender, System.EventArgs e)
	/// {
	///		if (postbackGuard.IsValid)
	///		{
	///			// Proceed normally
	///		}
	///		else
	///		{
	///			// Either the page has been refreshed or the previous postback is not complete
	///		}
	/// }
	/// ]]>
	/// </code>
	/// </example>
	[ToolboxData("<{0}:PostbackGuard runat=server />"), Designer(typeof(PostbackGuardDesigner))]
	public class PostbackGuard : System.Web.UI.WebControls.WebControl
	{
		#region Constants
		private const string HiddenFieldID = "hiddenPostbackGuard";
		#endregion

		#region Instance Variables
		private bool	isValid;
		#endregion

		#region Public Properties
		/// <summary>
		/// Indicates whether the page can proceed normally with the postback or not.
		/// </summary>
		[Description("Indicates whether the page can proceed normally with the postback or not")]
		public bool IsValid	{ get{ return isValid; } }
		#endregion

		#region Private Properties
		private static bool IsDesignMode	{ get { return HttpContext.Current == null; } }
		#endregion

		#region Overrides
		/// <summary>
		/// Determine if the postback is valid or not. It is valid if either the form collection does not
		/// contain the hidden field that this control registers, or the cache does not contain that field.
		/// The former handles postbacks and the later handles page refresh.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		protected override void OnInit(EventArgs e)
		{
			if (!IsDesignMode)
			{
				HttpContext context = HttpContext.Current;
				string key = context.Request.Form.Get(HiddenFieldID);
				isValid = ((key == null || context.Cache.Get(key) == null));
				if (isValid && (key != null)) 
					context.Cache.Insert(key, true, null, DateTime.Now.AddSeconds(context.Server.ScriptTimeout), TimeSpan.Zero);
			}
			base.OnInit(e);
		}

		/// <summary>
		/// Register the hidden field which will be used to detect postback and refresh.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);
			if (!IsDesignMode)
				Helper.RegisterHiddenFieldWithId(Page, HiddenFieldID, Guid.NewGuid().ToString());
		}

		#endregion
	}
	#endregion

	#region Class PostbackGuardDesigner
	/// <summary>
	/// <exclude/>
	/// Designer for the PostbackGuard control.
	/// </summary>
	/// <remarks>
	/// This class cannot be used directly.
	/// </remarks>
	public class PostbackGuardDesigner : ControlDesigner 
	{
		#region Overrides
		/// <summary>
		/// <exclude/>
		/// Return the place-holder HTML.
		/// </summary>
		/// <returns>HTML to render at design time</returns>
		public override string GetDesignTimeHtml() 
		{
			return CreatePlaceHolderDesignTimeHtml();
		}
		#endregion
	}
	#endregion
}

// --- EOF ---
